package travelDream.ejb;


import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBContext;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import travelDream.ejb.prodotto.base.EscursioneDTO;
import travelDream.ejb.prodotto.base.HotelDTO;
import travelDream.ejb.prodotto.base.TrasportoDTO;
import travelDream.ejb.prodotto.base.VoloDTO;
import travelDream.entities.Escursione;
import travelDream.entities.GiornatePredefinite;
import travelDream.entities.GiornateScelte;
import travelDream.entities.Hotel;
import travelDream.entities.PacchettoPredefinito;
import travelDream.entities.PacchettoScelto;
import travelDream.entities.Partecipanti;
import travelDream.entities.Trasporto;
import travelDream.entities.Partecipanti.PartecipantiPK;
import travelDream.entities.Utente;
import travelDream.entities.Volo;
import travelDream.ejb.gestionePacchetto.GiornataDTO;
import travelDream.ejb.gestionePacchetto.PacchettoDTO;

/**
 * Implementazione della registrazione utente
 */
@Stateless
public class GestioneUtente implements Gestione {

   
        @PersistenceContext
        private EntityManager em;
       
        @Resource
        private EJBContext context;
       
        @Override
        public void salva(UtenteDTO utenteDTO) {
               
                Utente utente = new Utente(utenteDTO);
                List<Gruppo> gruppo = new ArrayList<Gruppo>();
                gruppo.add(Gruppo.USER);
                utente.setGruppo(gruppo);
                em.persist(utente);
               
        }
       
        @Override
        @RolesAllowed({Gruppo._USER,Gruppo._ADMIN})
        public void aggiorna(UtenteDTO utente) {
                em.merge(new Utente(utente));
        }


        @Override
        @RolesAllowed({Gruppo._USER,Gruppo._ADMIN})
        public UtenteDTO getUtenteDTO() {
                UtenteDTO utenteDTO = convertToDTO(getPrincipalUser());
                return utenteDTO;
        }


        @Override
        @RolesAllowed({Gruppo._USER})
        public void unregister() {
                remove(getPrincipalUser());
        }
       
        public Utente cerca(String nickname) {
        return em.find(Utente.class, nickname);
    }
       
       
    public List<Utente> getTuttiUtenti() {
        return em.createNamedQuery(Utente.FIND_ALL, Utente.class)
                .getResultList();
    }

    public void remove(String email) {
                Utente utente = cerca(email);
        em.remove(utente);
        }
   
    public void remove(Utente utente) {
        em.remove(utente);
        }
   
   
    public Utente getPrincipalUser() {
        return cerca(getPrincipalNickname());
    }
   
   
       
    public String getPrincipalNickname() {
        return context.getCallerPrincipal().getName();
 
    }

    private UtenteDTO convertToDTO(Utente utente) {
                UtenteDTO utenteDTO = new UtenteDTO();
                utenteDTO.setEmail(utente.getEmail());
                utenteDTO.setNickname(utente.getNickname());
                return utenteDTO;
        }
   
    /*
	 * ******************************************************
	 * 
	 * METODI PER IL RECUPERO DEI PRODOTTI BASE DAL DATABASE  
	 *  
	 * ******************************************************
	 */
   
    /*
     * Ritorna dal database un voloDTO
     * @param id volo da recuperare dal database
     * @return
     */
    private VoloDTO getVoloById(int id){
                
        VoloDTO volo = new VoloDTO();
        try{
                        volo =convertEtoDTO(em.createNamedQuery("Volo.findById", Volo.class).setParameter("id", id).getSingleResult());
                   
                        return volo;
        }catch(NoResultException e){
                
                return volo;
        }
        }
   
    /*
     * Ritorna dal database un hotelDTO
     * @param id hotel da recuperare dal database
     * @return
     */
    private HotelDTO getHotelById(Integer id){
                HotelDTO hotel = new HotelDTO();
                try{
                        hotel = convertEtoDTO(em.createNamedQuery("Hotel.findById", Hotel.class).setParameter("id", id).getSingleResult());
                        
                       
                        return hotel;
                }catch(NoResultException e){
                        
                        return hotel;
                }
               
        }
   
    /*
     * Ritorna dal database un escursioneDTO
     * @param id escursione da recuperare dal database
     * @return
     */
    private EscursioneDTO getEscursioneById(Integer id){
                EscursioneDTO escursione = new EscursioneDTO();
                try{
                        escursione = convertEtoDTO(em.createNamedQuery("Escursione.findById", Escursione.class).setParameter("id", id).getSingleResult());
                        
                        return escursione;
                }catch(NoResultException e){
                        
                        return escursione;
                }
        }
    
    /*
     * Conversione di una Entity trasporto in un TrasportoDTO
     * 
     */
	public TrasportoDTO getTrasportoDTOByIdTrasporto(Integer idTrasporto){
		
        	try{
                TrasportoDTO trasporto = convertEtoDTO(getTrasportoByidTrasporto(idTrasporto));
                caricaTrasporto(trasporto);
                return trasporto;
        	}
        	catch(NullPointerException e){
        		return new TrasportoDTO();
        	}
	}
	
	/*
	 * Permette di recuperare dal database il trasporto con idTrasporto
	 */
	private Trasporto getTrasportoByidTrasporto(Integer idTrasporto){
		Trasporto trasporto = new Trasporto();
		try{
			trasporto = em.createNamedQuery("Trasporto.findByIdTrasporto", Trasporto.class).setParameter("idTrasporto", idTrasporto).getSingleResult();
			return trasporto;
		}catch(NoResultException e){
			
			return trasporto;
		}	
		
	}
	
	/*
	 * Viene caricato all'interno del TrasportoDTO
	 * il DTO del mezzo che i parametri tipologia e idMezzo descrivono
	 * 
	 */
	private void caricaTrasporto(TrasportoDTO trasporto){
		
		switch(trasporto.getTipologia()){
			case 0:
				trasporto.setVolo(getVoloById(trasporto.getIdMezzo()));
				break;
				
		}
		
	}
	
	/*
	 * ******************************************************
	 * 
	 * METODI PER LA CREAZIONE DEI PACCHETTI OGGETTI DALLE ENTITY  
	 *  
	 * ******************************************************
	 */
	
   
    /*
     * Ritorna una lista di giornate predefinite
     * @param idPacchetto, id del pacchetto in cui si trovano le giornate
     * @return
     */
    private List<GiornatePredefinite> getGiornatePredefinite(int idPacchetto){
        return em.createNamedQuery("GiornatePredefinite.findByPk", GiornatePredefinite.class).setParameter("idPacchetto", idPacchetto).getResultList();
    }
   
    /*
     * Ritorna una lista di giornate scelte
     * @param idPacchetto, id del pacchetto in cui si trovano le giornate
     * @return
     */
    private List<GiornateScelte> getGiornateScelte(int idPacchetto){
        return em.createNamedQuery("GiornateScelte.findByPk", GiornateScelte.class).setParameter("idPacchetto", idPacchetto).getResultList();
    }
   
    /*
     * Ritorna la lista dei pacchetti che appartengono ad un utente
     * @param idUtente
     * @return
     */
    private List<Partecipanti> getListaPacchettiUtenteDB(int idUtente){
        return em.createNamedQuery("Partecipanti.findByIdUtente", Partecipanti.class).setParameter("idUtente", idUtente).getResultList();
    }
   
   
    /**
     * Crea un pacchettoDTO dando come valore l'idPacchetto
     */
    public PacchettoDTO creaPacchettoSceltoOggetto(int idPacchetto){
       
        
        //Recupero la lista delle giornate di un pacchetto
        List<GiornateScelte> giornateScelteDB = getGiornateScelte(idPacchetto);
       
       
        PacchettoDTO pacchetto = new PacchettoDTO();
       //Per ogni giornata della lista creo il relativo oggetto giornata che possiede i prodotti base 
       //oggetti relativi e le informazioni generali
        for(GiornateScelte giorno : giornateScelteDB ){
                GiornataDTO giornataObj = new GiornataDTO();
                giornataObj.setEscursione(getEscursioneById(giorno.getIdEscursione()));
                giornataObj.setHotel(getHotelById(giorno.getIdHotel()));
                giornataObj.setTrasporto(getTrasportoDTOByIdTrasporto(giorno.getIdTrasporto()));
                giornataObj.setNumeroGiornata(giorno.getGiornataPK().getGiornata());
               
                pacchetto.addGiornata(giornataObj);
        }
       
        try{	//Recupero leinformazioni del pacchetto scelto dal DB per completare l'oggetto PacchettoDTO
                PacchettoScelto pacchettoScelto = em.createNamedQuery("PacchettoScelto.findById", PacchettoScelto.class).setParameter("idPacchetto", idPacchetto).getSingleResult();
 
                pacchetto.setNome(pacchettoScelto.getNome());
                pacchetto.setIdPacchetto(pacchettoScelto.getIdPacchetto());
                pacchetto.setDescrizione(pacchettoScelto.getDescrizione());
                pacchetto.setDataPartenza(pacchettoScelto.getDataPartenza());
                return pacchetto;
       
        }catch(NoResultException e){
                return pacchetto = null;
       
        }
    }
   
   
    /**
     * Crea un pacchettoDTO dando come valore l'idPacchetto
     */
    public PacchettoDTO creaPacchettoPredefinitoOggetto(int idPacchetto){
        //Recupero la lista delle giornate del pacchetto predefinito
        List<GiornatePredefinite> giornatePredefiniteDB = getGiornatePredefinite(idPacchetto);
        
        PacchettoDTO pacchetto = new PacchettoDTO();
        //Per ogni giornata della lista creo il relativo oggetto giornata che possiede i prodotti base 
        //oggetti relativi e le informazioni generali
        for(GiornatePredefinite giorno : giornatePredefiniteDB ){
                GiornataDTO giornataObj = new GiornataDTO();
                giornataObj.setEscursione(getEscursioneById(giorno.getIdEscursione()));
                giornataObj.setHotel(getHotelById(giorno.getIdHotel()));
                giornataObj.setTrasporto(getTrasportoDTOByIdTrasporto(giorno.getIdTrasporto()));
                giornataObj.setNumeroGiornata(giorno.getGiornataPK().getGiornata());
               
                pacchetto.addGiornata(giornataObj);
        }
       
        try{//Recupero leinformazioni del pacchetto predefinito dal DB per completare l'oggetto PacchettoDTO
        	PacchettoPredefinito pacchettoPredefinito = em.createNamedQuery("PacchettoPredefinito.findById", PacchettoPredefinito.class).setParameter("idPacchetto", idPacchetto).getSingleResult();
       
        	pacchetto.setNome(pacchettoPredefinito.getNome());
        	pacchetto.setIdPacchetto(pacchettoPredefinito.getIdPacchetto());
        	pacchetto.setDescrizione(pacchettoPredefinito.getDescrizione());
        	return pacchetto;
        }catch(NoResultException e){
        	return pacchetto = null;   
        }
    }
   

   
    /**
     * recupera la lista dei pacchetti posseduti dall'utente principale
     * seza creare le giornate
     */
    public List<PacchettoDTO> getPacchettiUtente(){
    	
        int idUtente = getPrincipalUser().getIdUtente();
        //Recupero la lista con i pacchetti posseduti dall'utente
        List<Partecipanti> pacchettiDB = getListaPacchettiUtenteDB(idUtente);
        List<PacchettoDTO> pacchettiUtenteObj = new ArrayList<PacchettoDTO>();
        //Per ogni pacchetto della lista creo il relativo pacchetto oggetto
        for(Partecipanti pacchetto : pacchettiDB){
               
                int idPacchetto = pacchetto.getPartecipantiPK().getIdPacchettoScelto();
                PacchettoScelto pacchettoScelto = em.createNamedQuery("PacchettoScelto.findById", PacchettoScelto.class).setParameter("idPacchetto", idPacchetto).getSingleResult();
                PacchettoDTO pacchettoObj = new PacchettoDTO();
               
                pacchettoObj.setIdPacchetto(pacchettoScelto.getIdPacchetto());
                pacchettoObj.setNome(pacchettoScelto.getNome());
                pacchettoObj.setDescrizione(pacchettoScelto.getDescrizione());
                pacchettoObj.setDataPartenza(pacchettoScelto.getDataPartenza());
                
                pacchettiUtenteObj.add(pacchettoObj);
        }
       
        return pacchettiUtenteObj;
    }
   
   
    /**
     * recupera la lista dei pacchetti predefiniti
     */
    public List<PacchettoDTO> getPacchettiPredefiniti(){
    	//Recupero la lista dei pacchetti predefiniti
        List<PacchettoDTO> pacchettiUtenteObj = new ArrayList<PacchettoDTO>();
        List<PacchettoPredefinito> pacchettiPredefiniti = em.createNamedQuery("PacchettoPredefinito.findAll", PacchettoPredefinito.class).getResultList();
        //Per ogni pacchetto creo il relativo oggetto
        for(PacchettoPredefinito pacchetto : pacchettiPredefiniti){
               
                PacchettoDTO pacchettoObj = new PacchettoDTO();
               
                pacchettoObj.setIdPacchetto(pacchetto.getIdPacchetto());
                pacchettoObj.setNome(pacchetto.getNome());
                pacchettoObj.setDescrizione(pacchetto.getDescrizione());
               
                pacchettiUtenteObj.add(pacchettoObj);
        }
       
        return pacchettiUtenteObj;
    }
   
   /**
    * Permette ad un utente di diventare partecipante del pacchetto
    * param: idPacchetto, pacchetto a cui si vuole partecipare
    */
    public void nuovoPartecipante(int idPacchetto){
        int idUtente = getPrincipalUser().getIdUtente();
        System.out.println(idUtente);  
        try{
                em.createNamedQuery("Partecipanti.SelectSpecific",Partecipanti.class).setParameter("idUtente",idUtente)
                                .setParameter("idPacchettoScelto", idPacchetto).getSingleResult();
        }catch(NoResultException e){
               
                PartecipantiPK partecipantePK = new PartecipantiPK();
                partecipantePK.setIdPacchettoScelto(idPacchetto);
                partecipantePK.setIdUtente(idUtente);
               
                Partecipanti partecipante = new Partecipanti();
                partecipante.setPartecipantiPK(partecipantePK);
               
                em.persist(partecipante);
        }      
    }
   
    public void salvaPacchetto(PacchettoDTO pacchetto){
    	
    	  
    	  
    	PacchettoScelto pack = new PacchettoScelto(pacchetto);
  		em.persist(pack);
  		em.flush();
  		pack = (PacchettoScelto) em.createQuery("SELECT p FROM PacchettoScelto p ORDER BY p.idPacchetto DESC").setMaxResults(1).getSingleResult();
  		
  		nuovoPartecipante(pack.getIdPacchetto());
  		
  		for(GiornataDTO gDTO : pacchetto.getGiornate()){
  			
  			GiornateScelte giornata = new GiornateScelte(pack.getIdPacchetto(),gDTO.getNumeroGiornata(),null,null,null);
  			em.persist(giornata);
  			giornata = convertDTOtoE(gDTO);
  			giornata.getGiornataPK().setIdPacchetto(pack.getIdPacchetto());
  			
  			//Dall'id del Volo estrae il trasporto!
  			Integer idTrasporto = null;
  			try{
  				int idMezzo = gDTO.getTrasporto().getVolo().getId();
  				Trasporto trasporto = em.createNamedQuery("Trasporto.findVoloByIdMezzo", Trasporto.class).setParameter("idMezzo", idMezzo).getSingleResult();
  				idTrasporto = trasporto.getIdTrasporto();
  			}
  			catch(NoResultException | NullPointerException e){
  			}
  			giornata.setIdTrasporto(idTrasporto);
  			updateGiornata(giornata);
  			em.flush();
  		}
  		System.out.println("Pacchetto Creato");  
    }
    
    private void updateGiornata(GiornateScelte g){
		Query query = em.createQuery("UPDATE GiornateScelte SET idEscursione = :idE, idHotel = :idH, idTrasporto = :idT "
				+ "WHERE giornataPK.idPacchetto = :idP AND giornataPK.giornata = :idG").
		setParameter("idP", g.getGiornataPK().getIdPacchetto()).
		setParameter("idG", g.getGiornataPK().getGiornata()).
		setParameter("idT", g.getIdTrasporto()).
		setParameter("idH", g.getIdHotel()).
		setParameter("idE", g.getIdEscursione());
		int result = query.executeUpdate();
	}
    
    /**
     * Elimina il pacchetto scelto e le sue giornate
     */
    public void eliminaPacchettoScelto(int idPacchetto){
    	
    	Partecipanti partecipante = new Partecipanti();
    	PartecipantiPK partecipantePK = new PartecipantiPK();
    	//Elimino dalla tabella partecipanti l'associazione utente pacchetto
    	partecipantePK.setIdPacchettoScelto(idPacchetto);
    	partecipantePK.setIdUtente(getPrincipalUser().getIdUtente());
    	partecipante.setPartecipantiPK(partecipantePK);
    	
    	Partecipanti part = em.merge(partecipante);
    	em.remove(part);
    	
    	boolean ciSonoAncoraPartecipanti = ciSonoAncoraPartecipanti(idPacchetto);
    	
    	//Se non ci sono piu partecipanti elimino il pacchetto dal data base
    	if(!ciSonoAncoraPartecipanti){
    		eliminaPacchettoSceltoDalDB(idPacchetto);
    	}
    }
    
    /*
     * Controlla se per un pacchetto scelto esistono ancora partecipanti 
     * @param idPacchetto
     * @return true , se ci sono ancora partecipanti, false altrimenti
     */
    private boolean ciSonoAncoraPartecipanti(int idPacchetto){
    	
    		List<Partecipanti> partecipanti = em.createNamedQuery("Partecipanti.findByIdPacchetto", Partecipanti.class)
    				.setParameter("idPacchetto", idPacchetto).getResultList();
    		
    		if(!partecipanti.isEmpty()){
    			for(Partecipanti par : partecipanti)
    				System.out.println(par.getPartecipantiPK().getIdUtente());
    			return true;
    		}
    	
    		return false;
    }
    
    /*
     * Elimina il pacchetto dal database
     */
    private void eliminaPacchettoSceltoDalDB(int idPacchetto){
    	
    	em.createNamedQuery("GiornateScelte.delete",GiornateScelte.class).setParameter("idPacchetto", idPacchetto).executeUpdate();
    	em.createNamedQuery("PacchettoScelto.delete",PacchettoScelto.class).setParameter("idPacchetto", idPacchetto).executeUpdate();
    }
    
    /*
	 * ******************************************************
	 * 
	 * METODI PER CREAZIONE DI LISTE
	 *  
	 * ******************************************************
	 */
    
    public List<VoloDTO> listaVolo(String luogo){
		List<VoloDTO> lista = new ArrayList<VoloDTO>();
		for(Volo v : listaVoliPrivate(luogo)){
			lista.add(convertEtoDTO(v));
		}
		return lista;
	}
	
	private List<Volo> listaVoliPrivate(String luogo){
		return em.createNamedQuery("Volo.findByLuogo", Volo.class).setParameter("luogo1", luogo).setParameter("luogo2", luogo).getResultList();
	}
	
	public List<VoloDTO> listaVoloConArrivo(String arrivo){
		List<VoloDTO> lista = new ArrayList<VoloDTO>();
		for(Volo v : listaVoliPrivateArrivo(arrivo)){
			lista.add(convertEtoDTO(v));
		}
		return lista;
	}
	
	private List<Volo> listaVoliPrivateArrivo(String arrivo){
		System.out.println(arrivo + "DB");
		return em.createNamedQuery("Volo.findByArrivo", Volo.class).setParameter("arrivo", arrivo).getResultList();
	}
	
	
	public List<VoloDTO> listaVoloConPartenza(String partenza){
		List<VoloDTO> lista = new ArrayList<VoloDTO>();
		for(Volo v : listaVoliPrivatePartenza(partenza)){
			lista.add(convertEtoDTO(v));
		}
		return lista;
	}
	
	private List<Volo> listaVoliPrivatePartenza(String partenza){
		System.out.println(partenza + "DB");
		return em.createNamedQuery("Volo.findByPartenza", Volo.class).setParameter("partenza", partenza).getResultList();
	}
	
	
	public List<VoloDTO> listaVoloConPartenzaEArrivo(String partenza, String arrivo){
		List<VoloDTO> lista = new ArrayList<VoloDTO>();
		for(Volo v : listaVoliPrivatePartenzaEArrivo(partenza,arrivo)){
			lista.add(convertEtoDTO(v));
		}
		return lista;
	}
	
	private List<Volo> listaVoliPrivatePartenzaEArrivo(String partenza, String arrivo){
		System.out.println(arrivo + "DB " + partenza + "DB");
		return em.createNamedQuery("Volo.findByPartenzaEArrivo", Volo.class).setParameter("partenza", partenza).setParameter("arrivo", arrivo).getResultList();
	}
    
	
	public List<HotelDTO> listaHotel(String luogo){
		List<HotelDTO> lista = new ArrayList<HotelDTO>();
		for(Hotel h : listaHotelPrivate(luogo)){
			lista.add(convertEtoDTO(h));
		}
		return lista;
	}
	
	private List<Hotel> listaHotelPrivate(String luogo) {
		return em.createNamedQuery("Hotel.findByLuogo", Hotel.class).setParameter("luogo", luogo).getResultList();
	}
	
	public List<EscursioneDTO> listaEscursione(String luogo){
		List<EscursioneDTO> lista = new ArrayList<EscursioneDTO>();
		for(Escursione e : listaEscursionePrivate(luogo)){
			lista.add(convertEtoDTO(e));
		}
		return lista;
	}
	
	private List<Escursione> listaEscursionePrivate(String luogo) {
		return em.createNamedQuery("Escursione.findByLuogo", Escursione.class).setParameter("luogo", luogo).getResultList();
	}
  
    /*
	 * ******************************************************
	 * 
	 * METODI PER CONVERTIRE UNA ENTITY IN OGGETTO  
	 *  
	 * ******************************************************
	 */
    private VoloDTO convertEtoDTO(Volo volo){
                return new VoloDTO(volo.getIdVolo(), volo.getDescrizione(), volo.getLuogoPartenza(),
                                volo.getLuogoArrivo(), volo.getOrarioPartenza(), volo.getOrarioArrivo(), volo.getCosto());
        }
   
    private HotelDTO convertEtoDTO(Hotel h){
                HotelDTO hotel = new HotelDTO(h.getIdHotel(),h.getNome(),h.getDescrizione(),
                                h.getContatti(), h.getIndirizzo(), h.getStelle(), h.getCosto(), h.getLuogo());
                return hotel;
        }
   
    private EscursioneDTO convertEtoDTO(Escursione e){
                EscursioneDTO escursione = new EscursioneDTO();
                escursione.setCosto(e.getCosto());
                escursione.setDescrizione(e.getDescrizione());
                escursione.setLuogo(e.getLuogo());
                escursione.setNome(e.getNome());
                escursione.setId(e.getIdEscursione());
                return escursione;
        }
    
	private TrasportoDTO convertEtoDTO(Trasporto trasporto){
		return new TrasportoDTO(trasporto.getIdTrasporto(), trasporto.getIdMezzo(), trasporto.getTipologia());
	}
   
	private GiornateScelte convertDTOtoE(GiornataDTO giornataDTO){
		return new GiornateScelte(giornataDTO);	
	}
}



